﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace ErrataParser
{
    class ErrataParser
    {
        private readonly Regex errataRegEx;

        private List<ErrataList> errataLists;

        private readonly Dictionary<string, ErrorType> ErrorTypeFromTypeIndicatorDictionary;

        //This pattern matches a single error in an errata list and separates out the error type
        //indicator, the page number, and the description of the error to build an HFError struct
        private const string defaultMatchingPattern =
            @"  \n
                (?<errorTypeIndicator>[{\[\(\<\?])	    
                (?<pageNumber>\d{1,3})			        
                (?:[}\]\)\>\?])[ ]                      
                (?<description>.+?                      
                (?=                                     
	                $                                   
                    |                                  
	                (?: \n        
	                    [{\[\(\<\?]                     
	                    \d{1,3}                         
	                    [}\]\)\>\?]                     
	                )
                ))";
//        const string defaultMatchingPattern =
//            @"                                          #Looks for the start of a pattern 
//                (?<errorTypeIndicator>[{\[\(\<\?])	    #Saves the character used to identify error type
//                (?<pageNumber>\d{1,3})			        #Saves the page number of the error
//                (?:[}\]\)\>\?])[ ]                      #Ignores the closing brace of the errorTypeIndicator
//                (?<description>.+?                      #Saves the remainder of the error as a description
//                (?=                                     #up until . . .
//	                $                                   #the end of the string/errataList
//                    |                                   #or
//	                (?: \                               #the next occurrence of a new line
//	                    [{\[\(\<\?]                     #followed by an error type indicator
//	                    \d{1,3}                         #followed by a page number
//	                    [}\]\)\>\?]                     #followed by a closing error indicator brace
//	                )
//                ))";

        const RegexOptions defaultRegexOptions =
            RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline;

        public ErrataParser(string errataPatternToMatch, RegexOptions regexOptionsToUse, Dictionary<string,                              ErrorType> ErrorTypeFromTypeIndicatorDictionary)
        {
            errataLists = new List<ErrataList>();
            this.ErrorTypeFromTypeIndicatorDictionary = ErrorTypeFromTypeIndicatorDictionary;
            errataRegEx = new Regex(errataPatternToMatch, regexOptionsToUse);
        }

        public ErrataParser()
            : this(defaultMatchingPattern, defaultRegexOptions, GetDefaultErrorTypeFromTypeIndicatorDictionary())
        {
        }

        public void LoadErrataLists()
        {
            errataLists = GenerateListOfErrataListsFromFilesInResources();
        }

        public List<HFError> GetListOfHFErrors()
        {
            return GetListOfHFErrors(errataLists);
        }

        public List<HFError> GetListOfHFErrors(IEnumerable<ErrataList> errataListsToParse)
        {
            var errataToReturn = new List<HFError>();
            foreach (var errataList in errataListsToParse)
            {
                errataToReturn.AddRange(GetListOfHFErrors(errataList));
            }
            return errataToReturn;
        }

        //Using the regular expression created in the ErrataParser() constructor, this public method
        //parses the given errataList by creating a new HFError to hold each match and then returning
        //a list of all such HFErrors in the given ErrataParser's errataList.
        public List<HFError> GetListOfHFErrors(ErrataList errataList)
        {
            const int approxNumHFErrorsToReturn = 200;
            var errataToReturn = new List<HFError>(approxNumHFErrorsToReturn);

            foreach (var match in errataRegEx.Matches(errataList.ErrataString))
            {
                //errorMatchGroups is a GroupCollection holding the three groups defined in the regular
                //expression above, which are the errorTypeIndicator, pageNumber, and description
                //groups. Each group in the collection can be converted to a string containing the
                //relevant part of the matching string.
                var errorMatchGroups = ((Match) match).Groups;
                
                //Build a new HFError from the RegEx Match and add it to the list to return
                errataToReturn.Add(new HFError(
                    int.Parse(errorMatchGroups["pageNumber"].ToString()),
                    ErrorTypeFromTypeIndicatorDictionary[errorMatchGroups["errorTypeIndicator"].ToString()],
                    errataList.ErrataExtent,
                    errorMatchGroups["description"].ToString()
                ));
            }

            return errataToReturn;
        }

        private static List<ErrataList> GenerateListOfErrataListsFromFilesInResources()
        {
            return new List<ErrataList>(4)
            {
                new ErrataList(
                    Properties.Resources.ChangesMadeInJan08Printing,
                    ErrorExtent.FixedInJanuary08Printing
                    ),
                new ErrataList(
                    Properties.Resources.ChangesMadeInApril08Printing,
                    ErrorExtent.FixedInApril08Printing
                    ),
                new ErrataList(
                    Properties.Resources.ConfirmedErrorsInMostRecentPrinting,
                    ErrorExtent.ConfirmedErrorInCurrentPrinting
                    ),
                new ErrataList(
                    Properties.Resources.UnconfirmedErrorsInMostRecentPrinting,
                    ErrorExtent.UnconfirmedError
                    )
            };
        }
        
        private static Dictionary<string, ErrorType> GetDefaultErrorTypeFromTypeIndicatorDictionary()
        {
            return new Dictionary<string, ErrorType>
            {
                {"[", ErrorType.SeriousTechnicalMistake},
                {"{", ErrorType.MinorTechnicalMistake},
                {"<", ErrorType.ImportantLanguageChange},
                {"(", ErrorType.MinorLanguageChange},
                {"?", ErrorType.ReaderQuestionOrClarification}
            };
        }
    }
}
